/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package cn.icanci.snow.spring.core;

import cn.icanci.snow.spring.aop.anno.Aspect;
import cn.icanci.snow.spring.core.anno.Component;
import cn.icanci.snow.spring.core.anno.Controller;
import cn.icanci.snow.spring.core.anno.Repository;
import cn.icanci.snow.spring.core.anno.Service;
import cn.icanci.snow.spring.util.ClassUtil;
import cn.icanci.snow.spring.util.ValidationUtil;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import java.lang.annotation.Annotation;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Bean Container
 * 
 * @author icanci
 * @date Created in 2021/05/19 17:00
 * @version 1.0
 */
@Slf4j
@SuppressWarnings("all")
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class BeanContainer {
    /**
     * 存放所有被标记的目标对象的Map
     */
    private final Map<Class<?>, Object>                    beanMap         = new ConcurrentHashMap<>();

    /**
     * 加载bean的注解列表
     */
    private static final List<Class<? extends Annotation>> BEAN_ANNOTATION = Arrays.asList(Component.class, Controller.class, Repository.class, Service.class, Aspect.class);

    /**
     * 容器是否被加载过
     */
    private boolean                                        loaded          = false;

    /**
     * 是否被加载过
     * @return 是否被加载
     */
    public boolean isLoaded() {
        return loaded;
    }

    /**
     * 获取Bean容器对象
     * @return 返回单例对象
     */
    public static BeanContainer getInstance() {
        return ContainerHolder.HOLDER.INSTANCE;
    }

    /**
     * 安全单例枚举
     */
    private enum ContainerHolder {
                                  HOLDER;

        private BeanContainer INSTANCE;

        ContainerHolder() {
            INSTANCE = new BeanContainer();
        }
    }

    /**
     * 获取bean的数量
     * @return bean 的数量
     */
    public int size() {
        return beanMap.size();
    }

    /**
     * 扫描加载所有的bean
     * @param packageName 包名
     */
    public synchronized void loadBeans(String packageName) {
        if (isLoaded()) {
            log.warn("bean container is loaded");
            return;
        }
        Set<Class<?>> classSet = ClassUtil.extractPackageClass(packageName);
        if (ValidationUtil.isEmpty(classSet)) {
            log.warn("extract nothing from packageName:{}", packageName);
            return;
        }
        for (Class<?> clazz : classSet) {
            for (Class<? extends Annotation> annotation : BEAN_ANNOTATION) {
                if (clazz.isAnnotationPresent(annotation)) {
                    beanMap.put(clazz, ClassUtil.newInstance(clazz, true));
                }
            }
        }
        loaded = true;
    }

    /**
     * 扫描加载所有的bean
     * @param packages 包名集合
     */
    public synchronized void loadBeansByPackages(List<String> packages) {
        if (isLoaded()) {
            log.warn("bean container is loaded");
            return;
        }
        for (String packageName : packages) {
            Set<Class<?>> classSet = ClassUtil.extractPackageClass(packageName);
            if (ValidationUtil.isEmpty(classSet)) {
                log.warn("extract nothing from packageName:{}", packageName);
                return;
            }
            for (Class<?> clazz : classSet) {
                for (Class<? extends Annotation> annotation : BEAN_ANNOTATION) {
                    if (clazz.isAnnotationPresent(annotation)) {
                        beanMap.put(clazz, ClassUtil.newInstance(clazz, true));
                    }
                }
            }
        }
        loaded = true;
    }

    /**
     * 添加一个class对象以及其Bean实例
     *
     * @param clazz Class 对象
     * @param bean Bean 实例 
     * @return 原有的 Bean 实例，没有就返回null
     */
    public Object addBean(Class<?> clazz, Object bean) {
        return beanMap.put(clazz, bean);
    }

    /**
     * 移除一个IOC容器管理的对象
     * @param clazz Class 对象
     * @return 删除的Bean实现，没有就返回null
     */
    public Object removeBean(Class<?> clazz) {
        return beanMap.remove(clazz);
    }

    /**
     * 返回Bean的实例
     * @param clazz Class对象
     * @return Bean的实例
     */
    public Object getBean(Class<?> clazz) {
        return beanMap.get(clazz);
    }

    /**
     * 根据注解筛选出Bean的Class集合
     *
     * @param annotation 注解
     * @return Class集合
     */
    public Set<Class<?>> getClassesByAnnotation(Class<? extends Annotation> annotation) {
        //1.获取beanMap的所有class对象
        Set<Class<?>> keySet = getClasses();
        if (ValidationUtil.isEmpty(keySet)) {
            log.warn("nothing in beanMap");
            return null;
        }
        //2.通过注解筛选被注解标记的class对象，并添加到classSet里
        Set<Class<?>> classSet = new HashSet<>();
        for (Class<?> clazz : keySet) {
            //类是否有相关的注解标记
            if (clazz.isAnnotationPresent(annotation)) {
                classSet.add(clazz);
            }
        }
        return classSet.size() > 0 ? classSet : null;
    }

    /**
     * 获取所有的IOC管理的Class集合
     * @return class集合
     */
    public Set<Class<?>> getClasses() {
        return beanMap.keySet();
    }

    /**
     * 获取所有 Bean 的集合
     * @return 获取所有的bean
     */
    public Set<Object> getBeans() {
        return new HashSet(beanMap.values());
    }

    /**
     * 根据注解筛选出Bean的Class集合
     * @param annotation 需要筛选的注解
     * @return Class 集合
     */
    public Set<Class<?>> getClassByAnnotation(Class<? extends Annotation> annotation) {
        // 获取beanMap所有的class对象
        Set<Class<?>> keySet = getClasses();
        if (ValidationUtil.isEmpty(keySet)) {
            log.warn("nothing in beanMap");
            return null;
        }
        // 通过注解筛选被注解标记的class对象，并添加到classSet里面去
        Set<Class<?>> classSet = new HashSet<>();
        for (Class<?> clazz : keySet) {
            if (clazz.isAnnotationPresent(annotation)) {
                classSet.add(clazz);
            }
        }
        return classSet.size() > 0 ? classSet : null;
    }

    /**
     * 根据接口或者父接口获取实现类或者子类的Class集合，不包含其本身
     * @param interfaceOrClass 需要筛选的注解
     * @return Class 集合
     */
    public Set<Class<?>> getClassBySuper(Class<?> interfaceOrClass) {
        // 获取beanMap所有的class对象
        Set<Class<?>> keySet = getClasses();
        if (ValidationUtil.isEmpty(keySet)) {
            log.warn("nothing in beanMap");
            return null;
        }
        // 判断 key set 里面的元素是否是传入的接口或者是类的子类，并添加到classSet里面去
        Set<Class<?>> classSet = new HashSet<>();
        for (Class<?> clazz : keySet) {
            if (interfaceOrClass.isAssignableFrom(clazz) && !clazz.equals(interfaceOrClass)) {
                classSet.add(clazz);
            }
        }
        return classSet.size() > 0 ? classSet : null;
    }
}
